//  On my honor:
//	
//  - I have not used source code obtained from another student, 
//    or any other unauthorized source, either modified or unmodified.  
//	
//  - All source code and documentation used in my program 
//    is either my original work, or was derived by me from the source
//    code published in the textbook for this course.
//	
//  - I have not discussed coding details about this project with anyone
//    other than my instructor, ACM/UPE tutors or the GTAs assigned to this
//    course. I understand that I may discuss the concepts of this program
//    with other students, and that another student may help me debug my
//    program so long as neither of us writes anything during the discussion
//    or modifies any computer file during the discussion.  I have violated
//    neither the spirit nor letter of this restriction.
//	

/**
 * Project 1 
 */

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.util.StringTokenizer;

/**
 * @author Eric Yoo, Christopher Suh
 * 
 */
public class Memman {

	/**
	 * Takes parameters <pool-size> <num-recs> and <command-file> in order.
	 * 
	 * @param args
	 *            command-line arguments
	 */
	public static void main(String[] args) {
		// check argument count
		if (args.length != 3) {
			System.err.println("Run memman with the proper arguments.");
			System.err
					.println("\tmemman <pool-size> <num-recs> <command-file>");
			System.err.println("pool-size\tsize of memory pool in bytes");
			System.err.println("num-recs\tnumber of records ");
			System.err.println("command-file\ttext file with commands");
		} else {
			if (!parseArguments(args)) {
				System.err.println("The program encountered an error while");
				System.err.println("attempting to process the file.");
			}
		}
	}

	/**
	 * parse the arguments passed to the program
	 */
	public static boolean parseArguments(String[] args) {
		MemManager manager;
		Handle[] record_array;
		String line = "";
		String fileName = args[2];
		byte[] data = new byte[256];

		// attempt to parse the memory pool and record array sizes from
		// program arguments
		try {
			manager = new MemManager(Integer.parseInt(args[0]));
			record_array = new Handle[Integer.parseInt(args[1])];
		} catch (NumberFormatException exc) {
			System.err.println("Run memman with the proper arguments.");
			System.err
					.println("\tmemman <pool-size> <num-recs> <command-file>");
			System.err.println("pool-size\tsize of memory pool in bytes");
			System.err.println("num-recs\tnumber of records ");
			System.err.println("command-file\ttext file with commands");
			return false;
		}
		// attempt to open the file
		try {
			// open the file with name fileName
			File inFile = new File(fileName);
			BufferedReader br = new BufferedReader(new InputStreamReader(
					new FileInputStream(inFile)));

			// iterate through file line by line
			while ((line = br.readLine()) != null) {
				StringTokenizer tokenizer = new StringTokenizer(line);

				// parse the lines by string tokens and process commands
				if (tokenizer.hasMoreTokens()) {
					String command = tokenizer.nextToken();

					// takes "insert record_number x y city_name" with any
					// whitespace format
					if (command.equals("insert")) {
						int rec_index = 0;

						try {
							rec_index = Integer.parseInt(tokenizer.nextToken());
						} catch (NumberFormatException exc) {
							System.err
									.println("Argument to insert command in wrong");
							System.err
									.println("format: Record index expected.");
							return false;
						}

						if (rec_index >= 0 && rec_index < record_array.length) {
							int x, y = 0;
							// attempt to parse x and y values from file
							try {
								x = (Integer.parseInt(tokenizer.nextToken()));
								y = (Integer.parseInt(tokenizer.nextToken()));
							} catch (NumberFormatException exc) {
								System.err
										.println("insert command called with x, y");
								System.err
										.println("coordinates in wrong format.");
								return false;
							}
							String cityName = tokenizer.nextToken();

							// allocate enough space for the data and fill the
							// byte array
							byte[] space = ByteBuffer
									.allocate(8 + cityName.length()).putInt(x)
									.putInt(y).put(cityName.getBytes()).array();

							// if record exists at position, remove that record
							// from memory first
							if (record_array[rec_index] != null) {
								System.out
										.println("Replace existing record at "
												+ rec_index + "\n");
								manager.remove(record_array[rec_index]);
							}

							Handle temp = manager.insert(space, space.length);

							// check if manager was able to insert data to
							// memory
							if (temp != null) {
								record_array[rec_index] = temp;
								System.out.println("Insert a new record at "
										+ rec_index + "\n");
							} else {
								System.out
										.println("No room for current insert command.");
								System.out.println("Record " + rec_index
										+ " will be empty.\n");
							}
						} else {
							System.err
									.println("remove command called with an index");
							System.err.println("value out of bounds.");
						}
					}

					// takes "remove record_number" with any whitespace format
					else if (command.equals("remove")) {
						int rec_index = 0;

						// attempt to parse the record number to be removed
						try {
							rec_index = Integer.parseInt(tokenizer.nextToken());

						} catch (NumberFormatException exc) {
							System.err.println("Wrong file format!");
							System.err.println("Please check the " + fileName
									+ " file");
							return false;
						}

						// check if record exists at rec_index
						if (rec_index >= 0 && rec_index < record_array.length) {
							if (record_array[rec_index] != null) {
								manager.remove(record_array[rec_index]);
								System.out.println("Remove record at "
										+ rec_index + "\n");
							}
							record_array[rec_index] = null;
						} else {
							System.err
									.println("remove command called with an index");
							System.err.println("value out of bounds.");
						}
					}

					// takes either "print" or "print record_number"
					else if (command.equals("print")) {
						// "print record_number"
						if (tokenizer.hasMoreTokens()) {
							int rec_index = 0;

							// attempt to parse the record number to be printed
							try {
								rec_index = Integer.parseInt(tokenizer
										.nextToken());
							} catch (NumberFormatException exc) {
								System.err
										.println("Wrong argument given for print command");
								System.err.println("Please check the "
										+ fileName + " file");
								return false;
							}

							// check record index boundary conditions
							if (rec_index >= 0
									&& rec_index < record_array.length) {
								if (record_array[rec_index] != null) {
									// request byte array with data using handle
									int size = manager.get(data,
											record_array[rec_index], 255);
									ByteBuffer temp = ByteBuffer.wrap(data);
									
									// format byte array data
									int x = temp.getInt();
									int y = temp.getInt();
									temp.position(8);
									String city = new String(temp.array());
									city = city.substring(8, size);

									// output formatted record
									System.out.printf(
											"%-6s --> " + rec_index + ": " + x
													+ ' ' + y + ' ' + city
													+ "\n\n",
											("["
													+ record_array[rec_index]
															.getID() + "]:"));
								}
							} else {
								System.err
										.println("print command called with an index");
								System.err.println("value out of bounds.");
							}
						}
						// "print"
						else {
							// iterate through records and print
							for (int i = 0; i < record_array.length; i++) {
								if (record_array[i] != null) {
									// request byte array with data using handle
									int size = manager.get(data,
											record_array[i], 255);
									
									// wrap and format byte array data
									ByteBuffer temp = ByteBuffer.wrap(data);
									int x = temp.getInt();
									int y = temp.getInt();
									temp.position(8);
									String city = new String(temp.array());
									city = city.substring(8, size);

									// output formatted record
									System.out
											.printf("%-6s --> " + i + ": " + x
													+ ' ' + y + ' ' + city
													+ '\n',
													("["
															+ record_array[i]
																	.getID() + "]:"));
								} else {
									System.out.println("           " + i
											+ ": [no record]");
								}
							}
							System.out.println();
							manager.dump();
						}
					} else {
						System.err
								.println("Unknown command or wrong file format.");
						System.err.println("Please check the " + fileName
								+ " file");
					}
				}
			}
			br.close();
		} catch (FileNotFoundException ex) {
			System.err.println("File does not exist!");
			return false;
		} catch (IOException ex) {
			System.err.println("IOException error");
			return false;
		}
		return true;
	}
}
